Skip to content

feat(work): WorkForm + Rule/RuleSet — slim re-cut of work system (R2-1)#1386

Merged
ohdearquant merged 2 commits into
mainfrom
feat/work-forms-rules
Jun 10, 2026
Merged

feat(work): WorkForm + Rule/RuleSet — slim re-cut of work system (R2-1)#1386
ohdearquant merged 2 commits into
mainfrom
feat/work-forms-rules

Conversation

@ohdearquant

Copy link
Copy Markdown
Owner

Summary

This PR is the R2-1 slim re-cut of the work system, superseding the lionagi/work/ scope from draft PRs #1231 and #1189. Those drafts are stale intent-references only — this PR is grounded entirely on main.

What's Kept vs. Cut

Component Decision Reason
WorkForm + FieldSpec KEPT Core form container with lifecycle, type coercion, and immutable-copy semantics
Rule + RuleSet KEPT Declarative validation rules (required/type/range/pattern/custom); no engine dependency
fill_form / validate_form KEPT Functional API over WorkForm; clean, stateless
WorkEngine / worker.py CUT Roadmap decision — next PR (engine layer)
definition.py CUT Only needed by engine; no dependency from WorkForm or RuleSet
CLI additions (cleanup.py, charter.py) CUT Roadmap decision

Design Choices vs. Drafts

  • ReDoS protection: draft used multiprocessing.Process (pickling issues on macOS spawn). Replaced with threading.Thread(daemon=True) — same timeout guarantee, no subprocess overhead, pickling not required.
  • No definition.py: the scope requirement explicitly said "include only if WorkForm genuinely needs it" — it doesn't.
  • No lionagi base class inheritance: WorkForm is a clean standalone Pydantic BaseModel. The draft predated recent refactors; forcing Element/Node inheritance would couple an engine-free primitive to the full protocol stack for no gain.

LOC Count

Module Lines
lionagi/work/__init__.py 56
lionagi/work/form.py 271
lionagi/work/rules.py 286
Source total 613
tests/work/test_work_forms_rules.py 794

Source is well under the 700 LOC target.

Test Evidence

106 passed in 1.65s   (work suite)
8527 passed, 1 skipped, 1 xfailed   (full suite — both pre-existing, zero regressions)

Supersedes the lionagi/work/ scope of #1231 and #1189. Does not merge the engine, worker, or CLI changes from those drafts.


🤖 Generated with Claude Code

Co-authored-by: Claude Fable 5 noreply@anthropic.com

ohdearquant and others added 2 commits June 10, 2026 11:57
#1189 work scope)

Introduces lionagi/work/ as a greenfield module with WorkForm, FieldSpec,
Rule, RuleSet — the validated core of the work system without the engine
or CLI layers. 106 new tests, all green against an 8527-test full suite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CRITICAL: remove false ReDoS thread-timeout protection (GIL blocks
join; daemon thread burns CPU regardless). Replace with honest doc +
tight 4096-char input cap. REGEX_MATCH_TIMEOUT removed from public API.

HIGH: validate_form/fill_form accept optional ruleset= kwarg. Rules run
after coercion; failures set status=error with errors in validation_errors.
validate_form without ruleset is unchanged.

MEDIUM: WorkForm and FieldSpec now inherit from Element, gaining UUID id,
created_at, and metadata consistent with lionagi ecosystem. form_id
becomes a str property alias over str(self.id).

LOW: FieldSpec rejects incompatible default at construction time (new
model_validator). RuleSet.add raises ValueError on duplicate rule_id.

Tests: 135 work tests (was 106), 8556 total passed (zero regressions).
New tests cover Element identity, default validation, ruleset integration,
duplicate-id prevention, bool-as-int behaviour (pinned), length cap, and
REGEX_MATCH_TIMEOUT absence assertion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ohdearquant

Copy link
Copy Markdown
Owner Author

Round 2 Review: lionagi/work module (PR #1386)

Summary

The fixes in this round successfully address the critical safety and architectural issues identified in Round 1. The dangerously misleading ReDoS "protection" has been replaced with honest documentation and sensible input limits. The WorkForm lifecycle is now correctly integrated with RuleSet validation, and the module has been aligned with the project's Element pattern.

Verification of Claimed Fixes

1. ReDoS "Theater" Removal [FIXED]

  • Observation: The thread-based timeout and REGEX_MATCH_TIMEOUT have been completely removed from rules.py.
  • Mitigation: A tighter REGEX_MAX_INPUT_LENGTH (4096) is enforced. More importantly, the module docstring now contains a prominent warning that pattern rules are for trusted patterns only and recommends google-re2 for untrusted inputs.
  • Verdict: Correct. Honesty in safety claims is preferred over "theater" that doesn't actually protect the GIL.

2. FormStatus and RuleSet Integration [FIXED]

  • Observation: validate_form() and fill_form() now accept an optional ruleset.
  • Validation: If a form passes FieldSpec checks but fails any rule in the ruleset, its status is correctly set to "error" and messages are appended to validation_errors.
  • Verdict: Correct. This resolves the high-severity disconnect where "validated" forms could still be invalid.

3. Element Pattern Alignment [FIXED]

  • Observation: WorkForm and FieldSpec now inherit from lionagi.protocols.generic.element.Element.
  • Verification: They now automatically possess UUID id, created_at timestamps, and a metadata dictionary. WorkForm.form_id is now a property alias for str(self.id).
  • Impact: This brings the work module into the core lionagi ecosystem as first-class citizens.
  • Note: This is a breaking change for constructor calls that previously used form_id="string". Users must now use id=UUID(...) or rely on auto-generated IDs and use title for human-readable labels.

4. FieldSpec Default Validation [FIXED]

  • Observation: FieldSpec now eagerly validates the default value against the declared type at construction time.
  • Verdict: Correct. Prevents downstream type errors during form filling.

5. RuleSet Duplicate Prevention [FIXED]

  • Observation: RuleSet.add() now raises ValueError if a rule_id is already present.
  • Verdict: Correct. Ensures rule set integrity.

6. Bool-as-Int Behavior [VERIFIED]

  • Observation: Tests now explicitly pin the behavior that type="int" accepts bool values (as they are subclasses of int in Python).
  • Verdict: Correct. Matches project-wide type coercion conventions.

Findings & Suggestions

[Minor] Serialization of form_id

WorkForm.form_id is now a @property, meaning it is excluded from model_dump() and to_dict() by default. While the underlying id is present, existing systems expecting the key "form_id" in JSON exports will need to be updated or use id.

  • Suggestion: If backward compatibility in serialized output is required, consider using pydantic.computed_field for the form_id property.

[Note] Breaking Change on WorkForm(form_id=...)

As verified during review, passing form_id to the WorkForm constructor now raises a ValidationError due to extra="forbid". This is an acceptable consequence of the Element migration, but should be noted in the changelog.

Test Results

  • uv run pytest tests/work/ -q passed (135 tests).
  • Full suite uv run pytest passed (8556 passed).
  • All fixes were empirically verified with reproduction scripts.

Final Verdict

The developer has addressed all critical and high-priority concerns with robust, idiomatic solutions.

Verdict: APPROVE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant